import handler.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import packet.impl.MessageRequestAbstractPacket;
import utils.LoginUtil;

import java.util.Calendar;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/**
 * @author zy.jordan
 */
public class NettyClient {
  private final static int MAX_RETRY = 5;

  public static void main(String[] args) {
    NioEventLoopGroup workGroup = new NioEventLoopGroup();
    Bootstrap bootstrap = new Bootstrap();

    bootstrap
        .group(workGroup)
        .channel(NioSocketChannel.class)
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
        .option(ChannelOption.SO_KEEPALIVE, true)
        .option(ChannelOption.TCP_NODELAY, true)
        .handler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) {
            ch.pipeline().addLast(new PacketDecoder());
            ch.pipeline().addLast(new LoginResponseHandler());
            ch.pipeline().addLast(new MessageResponseHandler());
            ch.pipeline().addLast(new PacketEncoder());
          }
        });

    connect(bootstrap, "localhost", 1527, MAX_RETRY);
  }

  private static void connect(final Bootstrap bootstrap, final String host, final int port) {
    bootstrap
        .connect(host, port)
        .addListener(future -> {
          if (future.isSuccess()) {
            System.out.println("连接成功");
          } else {
            connect(bootstrap, host, port);
          }
        });
  }

  private static void connect(final Bootstrap bootstrap, final String host, final int port, final int retry) {
    bootstrap
        .connect(host, port)
        .addListener(future -> {
          if (future.isSuccess()) {
            System.out.println("连接成功");
            Channel channel = ((ChannelFuture) future).channel();
            startConsoleThread(channel);
          } else {
            // 第几次连接
            int order = MAX_RETRY - retry + 1;
            // 本次连接的间隔
            int delay = 1 << order;
            System.err.println(Calendar.getInstance() + " : 连接失败, 第" + order + "次重连");
            bootstrap
                .config()
                .group()
                .schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS);
          }
        });
  }

  private static void startConsoleThread(Channel channel) {
    new Thread(() -> {
      System.out.println("输入消息发送至服务端：");
      while (!Thread.interrupted()) {
        if (LoginUtil.hasLogin(channel)) {
          Scanner scanner = new Scanner(System.in);
          String line = scanner.nextLine();

          channel.writeAndFlush(new MessageRequestAbstractPacket(line));
        }
      }
    }).start();
  }
}
